#include "httprequest.hpp"
bool HTTPRequest::onresponse(HTTPRequest* r,void* arg){
    static void* table[]={&&_ins_version,&&_ins_statuscode,&&_ins_statustext,
    &&_ins_statusend,&&_ins_key,&&_ins_keyhex1,&&_ins_keyhex2,&&_ins_value,
    &&_ins_valuehex1,&&_ins_valuehex2,&&_ins_valueend,&&_ins_resend};
    auto c=*(char*)arg;
	goto *table[r->mode];
    _ins_version:
	if(c!=' '){
		if(r->headcount<LENVERSION){
			r->version[r->headcount++]=c;
			return false;
		}
		r->invalidcallback(r,r->invalidarg);
		return false;
	}
	r->mode=ins_statuscode;
	r->headcount=0;
	return false;
	_ins_statuscode:
	if('0'<=c&&c<='9'){
		if(r->headcount<LENSTATUSCODE-1){
			r->statuscode[r->headcount++]=c;
			return false;
		}
		r->invalidcallback(r,r->invalidarg);
		return false;
	}
	if(c==' '){
		r->mode=ins_statustext;
		r->headcount=0;
		return false;
	}
	r->invalidcallback(r,r->invalidarg);
	return false;
	_ins_statustext:
	if(c!='\r'){
		if(r->headcount<LENSTATUSTEXT-1){
			r->statustext[r->headcount++]=c;
			return false;
		}
		r->invalidcallback(r,r->invalidarg);
		return false;
	}
	r->mode=ins_statusend;
	return false;
	_ins_statusend:
	if(c=='\n'){
		r->mode=ins_key;
		r->headcount=0;
		return false;
	}
	r->invalidcallback(r,r->invalidarg);
	return false;
	_ins_key:
	if(c=='%'){
		r->mode=ins_keyhex1;
		return false;
	}
	if(c=='\r'){
		r->mode=ins_resend;
		return false;
	}
	if(c!=':'){
		if(r->headcount<LENTABLEKEY-1){
			r->keybuffer[r->headcount++]=c;
			return false;
		}
		r->invalidcallback(r,r->invalidarg);
		return false;
	}
	r->mode=ins_value;
	r->headcount=0;
	return false;
	_ins_keyhex1:
	if('0'<=c&&c<='9'){
		r->mode=ins_keyhex2;
		r->hex=(c-'0')<<4;
		return false;
	}
	if('a'<=c&&c<='f'){
		r->mode=ins_keyhex2;
		r->hex=(c+(10-'a'))<<4;
		return false;
	}
	if('A'<=c&&c<='F'){
		r->mode=ins_keyhex2;
		r->hex=(c+(10-'A'))<<4;
		return false;
	}
	r->invalidcallback(r,r->invalidarg);
	return false;
	_ins_keyhex2:
	if('0'<=c&&c<='9'){
		if(r->headcount<LENTABLEKEY-1){
			r->keybuffer[r->headcount++]=r->hex+(c-'0');
			r->mode=ins_key;
			return false;
		}
		r->invalidcallback(r,r->invalidarg);
		return false;
	}
	if('a'<=c&&c<='f'){
		if(r->headcount<LENTABLEKEY-1){
			r->keybuffer[r->headcount++]=r->hex+(c+(10-'a'));
			r->mode=ins_key;
			return false;
		}
		r->invalidcallback(r,r->invalidarg);
		return false;
	}
	if('A'<=c&&c<='F'){
		if(r->headcount<LENTABLEKEY-1){
			r->keybuffer[r->headcount++]=r->hex+(c+(10-'A'));
			r->mode=ins_key;
			return false;
		}
		r->invalidcallback(r,r->invalidarg);
		return false;
	}
	r->invalidcallback(r,r->invalidarg);
	return false;
	_ins_value:
	if(c=='%'){
		r->mode=ins_valuehex1;
		return false;
	}
	if(c=='\r'){
		r->mode=ins_valueend;
		return false;
	}
	if(c!=':'){
		if(r->headcount<LENTABLEVALUE-1){
			r->valuebuffer[r->headcount++]=c;
			return false;
		}
		r->invalidcallback(r,r->invalidarg);
		return false;
	}
	r->mode=ins_value;
	r->headcount=0;
	return false;
	_ins_valuehex1:
	if('0'<=c&&c<='9'){
		r->mode=ins_valuehex2;
		r->hex=(c-'0')<<4;
		return false;
	}
	if('a'<=c&&c<='f'){
		r->mode=ins_valuehex2;
		r->hex=(c+(10-'a'))<<4;
		return false;
	}
	if('A'<=c&&c<='F'){
		r->mode=ins_valuehex2;
		r->hex=(c+(10-'A'))<<4;
		return false;
	}
	r->invalidcallback(r,r->invalidarg);
	return false;
	_ins_valuehex2:
	if('0'<=c&&c<='9'){
		if(r->headcount<LENTABLEVALUE-1){
			r->valuebuffer[r->headcount++]=r->hex+(c-'0');
			r->mode=ins_value;
			return false;
		}
		r->invalidcallback(r,r->invalidarg);
		return false;
	}
	if('a'<=c&&c<='f'){
		if(r->headcount<LENTABLEVALUE-1){
			r->valuebuffer[r->headcount++]=r->hex+(c+(10-'a'));
			r->mode=ins_value;
			return false;
		}
		r->invalidcallback(r,r->invalidarg);
		return false;
	}
	if('A'<=c&&c<='F'){
		if(r->headcount<LENTABLEVALUE-1){
			r->valuebuffer[r->headcount++]=r->hex+(c+(10-'A'));
			r->mode=ins_value;
			return false;
		}
		r->invalidcallback(r,r->invalidarg);
		return false;
	}
	r->invalidcallback(r,r->invalidarg);
	return false;
	_ins_valueend:
	if(c=='\n'){
		r->mode=ins_key;
		r->value.emplace(r->keybuffer,r->valuebuffer);
		memset(r->keybuffer,0,LENTABLEKEY);
		memset(r->valuebuffer,0,r->headcount);
		r->headcount=0;
		return false;
	}
	r->invalidcallback(r,r->invalidarg);
	return false;
	_ins_resend:
	if(c=='\n'){
		r->pack.callback(r,r->pack.arg);
		return true;
	}
	r->invalidcallback(r,r->invalidarg);
	return false;
}
int HTTPRequest::request(const char* address,unsigned int port,const std::string& method,
const std::string& url,const StringTable& header,void* data,size_t size,
RequestCallback callback,void* arg,bool same){
	if(!requested){
		if((!same||firsttime)&&connecthost(address,port)==-1)return -1;
		requested=true;
		firsttime=false;
		value.clear();
		memset(keybuffer,0,LENTABLEKEY);
		memset(valuebuffer,0,LENTABLEVALUE);
		memset(statuscode,0,LENSTATUSCODE);
		memset(statustext,0,LENSTATUSTEXT);
		memset(version,0,LENVERSION);
		mode=ins_version;
		headcount=0;
		pack.callback=callback,pack.arg=arg;
		auto ul=url.size(),vl=http1_1.size(),ml=method.size();
		auto hl=ul+vl+ml+6;//2组\r\n+空格
		for(auto &v:header)hl+=v.first.length()+v.second.length()+3;//一组\r\n+':'
		auto buffer=(char*)malloc(hl);
		pack.buffer=buffer;
		memcpy(buffer,method.c_str(),ml);
		buffer[ml]=' ';
		hl=ml+1;
		memcpy(buffer+hl,url.c_str(),ul);
		hl+=ul;
		buffer[hl++]=' ';
		memcpy(buffer+hl,http1_1.c_str(),vl);
		hl+=vl;
		buffer[hl++]='\r';
		buffer[hl++]='\n';
		for(auto& v:header){
			auto cur=&v.first;
			auto l=cur->length();
			memcpy(buffer+hl,cur->c_str(),l);
			hl+=l;
			buffer[hl++]=':';
			cur=&v.second;
			l=cur->length();
			memcpy(buffer+hl,cur->c_str(),l);
			hl+=l;
			buffer[hl++]='\r';
			buffer[hl++]='\n';
		}
		buffer[hl++]='\r';
		buffer[hl++]='\n';
		if(data){
			senddata({buffer,hl,(DataCallback)onrequest});
			senddata({(char*)data,size,emptysendback});
			recvdata({&got,1,(DataCallback)onresponse,&got});
			return 0;
		}
		senddata({buffer,hl,(DataCallback)onrequest});
		recvdata({&got,1,(DataCallback)onresponse,&got});
		return 0;
	}
	return -1;
}
void HTTPRequest::getdata(void* buffer,size_t size,RequestCallback callback,void* arg){
	recvdata({(char*)buffer,size,(DataCallback)callback,arg});
}
void HTTPRequest::print(){
	char buffer[LENVERSION+1];
	memcpy(buffer,version,LENVERSION);
	buffer[LENVERSION]=0;
	printf("%s %s %s\n",buffer,statuscode,statustext);
	for(auto& v:value)printf("%s:%s\n",v.first.c_str(),v.second.c_str());
}
HTTPRequest::HTTPRequest(TimeoutCallback timeout,void* timeoutarg,
RequestCallback invalidcallback,void* invalidarg,size_t maxtime):
invalidcallback(invalidcallback),invalidarg(invalidarg),Client(
DataPack{nullptr,0,nullptr,nullptr},(Client::TimeoutCallback)timeout,
timeoutarg,maxtime),requested(false),firsttime(true){pack.buffer=nullptr;}
HTTPRequest::~HTTPRequest(){if(pack.buffer)free(pack.buffer);}
static const std::string _0("0"),_t("Content-Length");
const StringTable HTTPRequest::defaulttable{{_t,_0}};

bool HTTPRequest::openlib(){
	return Client::openlib()&openutil();
}
void HTTPRequest::closelib(){
	Client::closelib(),closeutil();
}
bool HTTPRequest::onrequest(HTTPRequest* r,void* arg){
	auto& pack=r->pack;
	free(pack.buffer);
	pack.buffer=nullptr;
	return true;
}